"use strict";

import * as constant from "./constant";
import {Packet} from "./packet";
import {Utils} from "./utils";

class Client {
    callback = {};

    constructor(url, readyStateCallback) {
        if (!("WebSocket" in window)) {
            return;
        }

        this.retryInterval = constant.RETRY_INTERVAL;
        this.maxRetryTimes = constant.MAX_RETRY_TIMES;
        this.retryTimes = 0;
        this.requestHeader = "";
        this.maxPayload = constant.MAX_PAYLOAD;
        this.socket = this.connect(url, readyStateCallback);
    }

    // 向服务端发送ping包保持长连接
    ping(interval, param, callback) {
        if (typeof(callback) !== "object") {
            throw "callback must be an object";
        }

        if (this.socket.readyState !== this.socket.OPEN) {
            throw "asyncSend: connection refuse";
        }

        let _this = this;
        this.addMessageListener(0, function (data) {
            let code = _this.getResponseProperty("code");
            if (typeof code !== "undefined") {
                let message = _this.getResponseProperty("message");
                if (callback.hasOwnProperty("onError") && typeof callback.onError === "function") {
                    callback.onError(code, message);
                }
            } else {
                if (callback.hasOwnProperty("onSuccess") && typeof callback.onSuccess === "function") {
                    callback.onSuccess(data);
                }
            }

            if (callback.hasOwnProperty("onEnd") && typeof callback.onEnd === "function") {
                callback.onEnd();
            }
        });

        this.t1 = setInterval(function () {
            _this.socket.send(Packet.pack(0, 0, this.requestHeader, JSON.stringify(param)));
        }, interval);
    }

    // 异步请求服务端数据
    asyncSend(operator, param, callback) {
        if (typeof(callback) !== "object") {
            throw "callback must be an object";
        }

        if (this.socket.readyState !== this.socket.OPEN) {
            throw "asyncSend: connection refuse";
        }

        if (callback.hasOwnProperty("onStart") && typeof callback.onStart === "function") {
            callback.onStart();
        }

        let _this = this;
        let sequence = (new Date()).getTime();
        let listener = Utils.crc32(operator) + sequence;
        this.callback[listener] = function (data) {
            let code = _this.getResponseProperty("code");
            if (typeof code !== "undefined") {
                let message = _this.getResponseProperty("message");
                if (callback.hasOwnProperty("onError") && typeof callback.onError === "function") {
                    callback.onError(code, message);
                }
            } else {
                if (callback.hasOwnProperty("onSuccess") && typeof callback.onSuccess === "function") {
                    callback.onSuccess(data);
                }
            }

            if (callback.hasOwnProperty("onEnd") && typeof callback.onEnd === "function") {
                callback.onEnd();
            }

            delete _this.callback[listener];
        };

        this.socket.send(Packet.pack(Utils.crc32(operator), sequence, this.requestHeader, JSON.stringify(param)));
    }

    // 同步请求服务端数据
    async syncSend(operator, param, callback) {
        await this.asyncSend(operator, param, callback);
    }

    // 添加消息监听
    addMessageListener(operator, listener) {
        this.callback[Utils.crc32(operator)] = listener;
    }

    // 移除消息监听
    removeMessageListener(operator) {
        delete this.callback[Utils.crc32(operator)];
    }

    // 获取socket的链接状态
    getReadyState() {
        return this.socket.readyState;
    }

    // 设置单个请求能够处理的最大字节数
    setMaxPayload(maxPayload) {
        this.maxPayload = maxPayload;
    }

    // 设置断线重连的时间间隔
    setInterval(interval) {
        this.retryInterval = interval;
    }

    // 设置最大重连次数
    setMaxRetryTimes(times) {
        this.maxRetryTimes = times;
    }

    // 设置请求属性
    setRequestProperty(key, value) {
        let v = this.getRequestProperty(key);
        if (v !== "") {
            this.requestHeader = this.requestHeader.replace(key + "=" + value + ";", "");
        }

        this.requestHeader = this.requestHeader + key + "=" + value + ";";
    }

    // 获取请求属性
    getRequestProperty(key) {
        let values = this.requestHeader.split(";");
        for (let index in values) {
            let kv = values[index].split("=");
            if (kv[0] === key) {
                return kv[1];
            }
        }
    }

    // 设置Response属性
    setResponseProperty(key, value) {
        let v = this.getResponseProperty(key);
        if (v !== "") {
            this.responseHeader = this.responseHeader.replace(key + "=" + value + ";", "");
        }

        this.responseHeader = this.responseHeader + key + "=" + value + ";";
    }

    // 获取响应属性
    getResponseProperty(key) {
        let values = this.responseHeader.split(";");
        for (let index in values) {
            let kv = values[index].split("=");
            if (kv[0] === key) {
                return kv[1];
            }
        }
    }

    // 创建连接
    connect(url, readyStateCallback) {
        let ws = new WebSocket(url);
        let _this = this;
        ws.binaryType = "blob";

        if (readyStateCallback.hasOwnProperty("onopen") && typeof readyStateCallback.onopen === "function") {
            ws.onopen = function (ev) {
                readyStateCallback.onopen(ev);
                _this.retryTimes = 0;
            };
        }

        if (readyStateCallback.hasOwnProperty("onclose") && typeof readyStateCallback.onclose === "function") ws.onclose = function (ev) {
            clearInterval(_this.t1);

            _this.t2 = setInterval(function () {
                _this.retryTimes++;
                if (_this.retryTimes > _this.maxRetryTimes) {
                    clearInterval(_this.t2);
                } else {
                    _this.socket = _this.connect(url, readyStateCallback);
                }
            }, _this.retryInterval);

            readyStateCallback.onclose(ev);
        };

        if (readyStateCallback.hasOwnProperty("onerror") && typeof  readyStateCallback.onerror === "function") {
            ws.onerror = function (ev) {
                readyStateCallback.onerror(ev);
            };
        }

        ws.onmessage = function (ev) {
            if (ev.data instanceof Blob) {
                let reader = new FileReader();
                reader.readAsArrayBuffer(ev.data);
                reader.onload = function () {
                    let packet = Packet.unPack(this.result);
                    let packetLength = packet.headerLength + packet.bodyLength + 20;
                    if (packetLength > constant.MAX_PAYLOAD) {
                        throw "the packet is big than " + constant.maxPayload;
                    }

                    let operator = Number(packet.operator) + Number(packet.sequence);
                    if (_this.callback.hasOwnProperty(operator)) {
                        if (packet.body === "") {
                            packet.body = "{}";
                        }
                        _this.responseHeader = packet.header;
                        _this.callback[operator](JSON.parse(packet.body));
                    }
                };
            } else {
                throw "unsupported data format";
            }
        };

        return ws;
    }
}


export {Client};
